/*
 * Description: process preload handler
 */
#include "perf_process_preload.h"
#include <Windows.h>
#include <userenv.h>
#include <set>
#include <fstream>
#include <vector>
#include <tlhelp32.h>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <memory>
#include <functional>
#include <shlwapi.h>
#include "error.h"
#include "log.h"
#include "common/guest_utils.h"
#include "performance/perf_constants.h"
#include "common/text_utils.h"
#include "performance/utils/perf_utils.h"
#pragma comment(lib, "userenv.lib")
#pragma comment(lib, "shlwapi.lib")
using namespace COMMON;

namespace PERF {

std::string g_processCmdlineAcad;                                // Acad启动命令
std::string g_processCmdlineXmind;                               // Xmind启动命令
std::string g_processFilePath;
DWORD g_processIdAcad = 0;                                       // Acad预加载进程ID，为0时代表不存在预加载进程
DWORD g_processIdXmind = 0;                                      // Xmind预加载进程ID，为0时代表不存在预加载进程

static std::atomic<bool> g_isProcessHasShowAcad = false;         // Acad预拉起进程窗口是否显示标志位
static std::atomic<bool> g_isProcessHasShowXmind = false;        // Xmind预拉起进程窗口是否显示标志位
static std::atomic<bool> g_isProcessHasShowPs = false;           // PS预拉起进程窗口是否显示标志位
static std::atomic<bool> g_isAcadPreloadTimerExist = false;      // 预拉起Acad进程定时器是否存在标志位
static std::atomic<bool> g_isAcadNeedCloseTimerExist = false;    // 预拉起Acad进程关闭定时器是否存在标志位
static std::atomic<bool> g_isXmindPreloadTimerExist = false;     // 预拉起Xmind进程定时器是否存在标志位
static std::atomic<bool> g_isXmindNeedCloseTimerExist = false;   // 预拉起Xmind进程关闭定时器是否存在标志位
static std::atomic<bool> g_isXmindFilePreloadOpen = false;       // xmind文件是否使用预加载进程打开标志位
static std::atomic<bool> g_isPsTimmerStart = false;              // photoshop文件是否使用预加载进程打开标志位
static std::atomic<bool> g_isPsPreload = false;                  // photoshop是否为预加载进程
static int g_preloadTimerCount = 240;                            // 预拉起定时器执行2个小时关闭定时器240*30=2h
static int g_pspreloadTimerCount = 240;                          // 预拉起定时器执行2个小时关闭定时器240*30=2h
static int g_xmindPreloadTimerCount = 240;                       // 预拉起定时器执行2个小时关闭定时器240*30=2h
static int g_xmindFileCloseTimerCount = 240;                     // 预拉起定时器执行2个小时关闭定时器240*30=2h
static int g_isProcessNeedCloseCount = 120;                      // 预拉起定时器执行2个小时关闭定时器120*1=2h
static int g_isXmindProcessNeedCloseCount = 120;                 // 预拉起定时器执行2个小时关闭定时器120*1=2h

const std::vector<std::string> NEED_PRELOAD_PROCESS = {"acad.exe", "photoshop.exe", "xmind.exe"};  // 预拉起目标进程
const std::vector<std::string> COMMON_PRELOAD_PROCESS = {"acad.exe", "Xmind.exe"};                 // 目标进程名称
const int ACAD_INDEX = 0;
const int XMIND_INDEX = 1;
const int ACAD_PRELOAD_INDEX = 0;
const int PS_PRELOAD_INDEX = 1;
const int XMIND_PRELOAD_INDEX = 2;
const int XMIND_BASIC_PROCESS = 6;                                // Xmind基础进程数量
const int PSTIMER = 240;                                          // PS预加载定时器重置，240*30=2h
const int SPLIT_END_POS_QU = 3;                                   // 截取文件路径最后位置，从第二位截取
const int SPLIT_END_POS = 4;                                      // 截取文件路径最后位置，从第一位截取
const int SPLIT_BEGIN_POS_QU = 1;                                 // 截取文件路径开始位置，从第二位截取
const int SPLIT_BEGIN_POS = 0;                                    // 截取文件路径开始位置，从第一位截取

static const std::string XMIND_FILE_SUFFIX = ".xmind";            // Xmind文件后缀
static const std::string PARAM_SEP = " ";                         // 参数分隔符
static const std::string PARAM_MARKER = "\"";                     // 引号分隔符
static const std::string ACTION_SHOW = "show";                    // acad显示命令行
static const std::string ACTION_SHOW_AND_REOPEN_FILE = "reopen";  // acad文档打开防呆参数
static const std::string ACTION_PS_WINDOW = " pswindow ";         // photoshop窗口检查参数
static const std::string ACTION_PS_START = " startps ";             // photoshop预加载参数
static const std::string ACTION_CAD_WINDOW = " cadwindow ";         // auto登录窗口检查参数
const std::string PRELOAD_UNUSE_CMD_LINE = "UNUSECMD";
const std::string PHOTOSHOP_PRE = "Photoshop.exe";                // 后台PS进程名字
const std::string PS_CONTROL = "ps_process_control.exe";          // 用来控制PS预加载以及显示窗口
const std::string START_NOLOGO = " /nologo";                      // acad预拉起参数
const std::string PRELOAD_EXPLORER_EXE_NAME = "explorer.exe";     // explorer.exe进程，用来获取user token
const std::string PRE_PROCESS_WNDCONTROL_EXE_PATH = "C:\\ProgramData\\performance\\pre_process_wndcontrol.exe";
const std::string PS_PRE_PROCESS_WNDCONTROL_EXE_PATH = "C:\\ProgramData\\performance\\ps_process_control.exe";
const std::string PHOTOSHOP_WORKSPACE_PREFS_PSP_PATH_FORMAT =
    R"(C:\Users\%s\AppData\Roaming\Adobe\Adobe Photoshop 2020)"
    R"(\Adobe Photoshop 2020 Settings\Workspace Prefs.psp)";

const DWORD CHECK_PROCESS_DUE_THIRTY_SECONDS = 30;                // 检测进程是否存在,不存在则拉起,定时器30秒后触发
const DWORD CHECK_PROCESS_RESET_PID_TWO_SECONDS = 2;  // 右键、双击打开dwg文件后，2s等待周期，用来规避新触发的进程
const DWORD CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD = 60;   // 每1分钟触发1次进程定时器，检测是否需要关闭预加载进程
const DWORD PRELOAD_MAX_DURATION_TWO_HOURS = 7200;                // 2小时检测进程未被使用，则删除
const DWORD PRELOAD_MAX_DURATION_ONE_HOURS_AND_FIFTY_EIGHT = 7080;                // 1小时58分检测进程未被加载，则加载
const DWORD PROCESS_DURATION_SECOND_BASE = 1000000;               // us转换s的基数
const DWORD TERMINATE_PROCESS_FLAG = 1;                           // 1为进程的退出码
const DWORD SYS_MEMORY_THRESHOLD = 80;                            // 预加载系统内存占比水线
const DWORD WAIT_EXIT_TIME_MS = 200;                              // 进程退出等待时间 ms

void PreloadTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]process preload timer start");
    g_isAcadPreloadTimerExist = true;
    std::thread([interval, callback]() {
        while (g_preloadTimerCount > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(interval));
            --g_preloadTimerCount;
            if (g_preloadTimerCount > 0) {
                callback();  // 调用回调函数
            }
        }
        g_isAcadPreloadTimerExist = false;
        LOG_INFO("[PRELOAD]process preload timer end");
    }).detach();
}

void ProcessPreload::PsPreloadTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]PsPreloadTimerTask start");
    std::thread([this, interval, callback]() {
        g_isPsTimmerStart = true;
        while (g_pspreloadTimerCount > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(interval));
            --g_pspreloadTimerCount;
            if (g_pspreloadTimerCount > 0) {
                callback();  // 调用回调函数
            }
        }
        LOG_INFO("[PRELOAD]PsPreloadTimerTask finish after two hours");
        if ((PERF_PROCESS_PRELOAD.IsTargetProcessExist(PHOTOSHOP_PRE)) && !g_isProcessHasShowPs) {
            LOG_INFO("[PRELOAD]two hours of photoshop in the background , exit photoshop");
            std::vector<DWORD> psProcess = PERF_PROCESS_PRELOAD.GetAllProcessIdByName(PHOTOSHOP_PRE);
            for (auto psPro : psProcess) {
                ClosePreloadProcess(psPro);
            }
        }
        g_isPsTimmerStart = false;
    }).detach();
}

void PreloadCheckIsNeedCloseTimerTask(std::function<void()> callback, int interval)
{
    g_isAcadNeedCloseTimerExist = true;
    LOG_INFO("[PRELOAD]process check is need close timer start");
    std::thread([interval, callback]() {
        while (g_isProcessNeedCloseCount > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(interval));
            --g_isProcessNeedCloseCount;
        }
        callback();  // 调用回调函数
        g_isAcadNeedCloseTimerExist = false;
        LOG_INFO("[PRELOAD]process check is need close timer end");
    }).detach();
}

void PreloadSuspendTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]process preload suspend timer start");
    std::thread([interval, callback]() {
        std::this_thread::sleep_for(std::chrono::seconds(interval));
        if (!g_isProcessHasShowAcad) {
            callback();
        }
        LOG_INFO("[PRELOAD]process preload suspend timer end");
    }).detach();
}

void PreloadResetPidTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]process preload reset g_process_id timer start");
    std::thread([interval, callback]() {
        std::this_thread::sleep_for(std::chrono::seconds(interval));
        callback();
        LOG_INFO("[PRELOAD]process preload reset g_process_id timer end");
    }).detach();
}

void ProcessPreload::ProcessPreloadEntryTimerFunc()
{
    BOOL bRet = PERF_PROCESS_PRELOAD.IsTargetProcessExist(COMMON_PRELOAD_PROCESS[ACAD_INDEX]);
    if (!bRet && !MemoryUsageLimitExceed()) {
        PERF_PROCESS_PRELOAD.PreloadProcessHide(COMMON_PRELOAD_PROCESS[ACAD_INDEX]);
    }
}

void ProcessPreloadResetPidTimerFunc()
{
    g_processIdAcad = 0;
}

void ProcessSuspendAndSetMemFunc()
{
    LOG_INFO("[PRELOAD]start to close the Event Insights Window");
    DWORD exitCodeCAD = 1;
    std::string cadWindowLine = PS_PRE_PROCESS_WNDCONTROL_EXE_PATH + ACTION_CAD_WINDOW;
    GuestUtils::ExecCmdWithExitCode(cadWindowLine, true, exitCodeCAD);
    LOG_INFO("[PRELOAD]exitCodeCAD{}, process Suspend&WorkingSet start excute", exitCodeCAD);
    if (g_processIdAcad == 0 || g_isProcessHasShowAcad) {
        LOG_ERR("[PRELOAD]Failed To excute suspend autocad");
        return;
    }
    bool bRet = PERF_PROCESS_PRELOAD.SuspendOrResumeProcessByName(PNAME_AUTOCAD, g_processIdAcad, true);
    if (!bRet) {
        LOG_INFO("[PRELOAD]process Suspend excute failed");
        return;
    }

    bRet = PERF_PROCESS_PRELOAD.SetProcessWorkingSet(g_processIdAcad);
    if (!bRet) {
        LOG_INFO("[PRELOAD]process Set WorkingSet failed");
    }
}

void ProcessCheckNeedCloseTimerFunc()
{
    if (g_processIdAcad == 0) {
        LOG_INFO("[PRELOAD]g_processIdAcad is not valid");
        return;
    }
    LOG_INFO("[PRELOAD]process check is need close timer start excute");
    if (!g_isProcessHasShowAcad) {
        PERF_PROCESS_PRELOAD.ClosePreloadProcess(g_processIdAcad);
        g_processIdAcad = 0;
        return;
    }
}

void ProcessPreload::SuspendOrResumeProcessByThread(DWORD processId, bool isNeedSuspend)
{
    THREADENTRY32 th32 = {0};
    th32.dwSize = sizeof(THREADENTRY32);

    HANDLE hThreadSanp = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
    if (hThreadSanp == INVALID_HANDLE_VALUE) {
        LOG_ERR("[PRELOAD]Failed To Get Thread SnapShot");
        return;
    }
    Thread32First(hThreadSanp, &th32);
    do {
        if (th32.th32OwnerProcessID != processId) {
            continue;
        }
        HANDLE hth = OpenThread(THREAD_ALL_ACCESS, FALSE, th32.th32ThreadID);
        if (hth == INVALID_HANDLE_VALUE) {
            continue;
        }
        if (isNeedSuspend) {
            SuspendThread(hth);
        } else {
            ResumeThread(hth);
        }
        SAFE_CLOSE_HANDLE(hth, INVALID_HANDLE_VALUE);
    } while (Thread32Next(hThreadSanp, &th32));

    SAFE_CLOSE_HANDLE(hThreadSanp, INVALID_HANDLE_VALUE);
    return;
}

bool ProcessPreload::SuspendOrResumeProcessByName(const std::string& processName, DWORD processId, bool isNeedSuspend)
{
    LOG_INFO("[PRELOAD] SuspendOrResumeProcessByName start!\n");
    PROCESSENTRY32 pe32 = {0};
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hProcessSanp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSanp == INVALID_HANDLE_VALUE || processId == 0) {
        LOG_ERR("[PRELOAD]Failed To Get the Process SnapShot or processId is invalid");
        return false;
    }

    Process32First(hProcessSanp, &pe32);
    do {
        std::string szExeFile = pe32.szExeFile;
        if ((strncmp(szExeFile.c_str(), processName.c_str(), strlen(szExeFile.c_str())) == 0) ||
            (strncmp(szExeFile.c_str(), processName.c_str(), strlen(processName.c_str())) == 0)) {
            SuspendOrResumeProcessByThread(processId, isNeedSuspend);
        }
    } while (Process32Next(hProcessSanp, &pe32));
    SAFE_CLOSE_HANDLE(hProcessSanp, INVALID_HANDLE_VALUE);
    LOG_INFO("[PRELOAD] SuspendOrResumeProcessByName end!\n");
    return true;
}

DWORD ProcessPreload::GetProcessRunningDuration(DWORD processId)
{
    LOG_INFO("[PRELOAD]get process running duration start");
    DWORD userSeconds = 0;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
    if (hProcess != INVALID_HANDLE_VALUE) {
        FILETIME createTime;
        FILETIME exitTime;
        FILETIME kernelTime;
        FILETIME userTime;
        if (GetProcessTimes(hProcess, &createTime, &exitTime, &kernelTime, &userTime)) {
            ULARGE_INTEGER time;
            time.LowPart = userTime.dwLowDateTime;
            time.HighPart = userTime.dwHighDateTime;
            userSeconds = time.QuadPart / PROCESS_DURATION_SECOND_BASE;
        }
        SAFE_CLOSE_HANDLE(hProcess, INVALID_HANDLE_VALUE);
    } else {
        userSeconds = PRELOAD_MAX_DURATION_TWO_HOURS;
    }
    return userSeconds;
}

bool ProcessPreload::IsTargetProcessExist(const std::string &name)
{
    PROCESSENTRY32 pe32 = {0};
    pe32.dwSize = sizeof(PROCESSENTRY32);
    // 获取全部进程快照
    bool existFlag = false;
    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        LOG_ERR("[PRELOAD]target process check  hProcessSnap is null or invalid");
        return existFlag;
    }
    // 获取快照中第一条信息
    BOOL bRet = ::Process32First(hProcessSnap, &pe32);
    while (bRet) {
        const std::string szExeFile = pe32.szExeFile;
        if ((strncmp(szExeFile.c_str(), name.c_str(), strlen(szExeFile.c_str())) == 0) ||
            (strncmp(szExeFile.c_str(), name.c_str(), strlen(name.c_str())) == 0)) {
            existFlag = true;
            break;
        }
        // 获取快照中下一条信息
        bRet = ::Process32Next(hProcessSnap, &pe32);
    }
    if (!existFlag) {
        LOG_INFO("[PRELOAD]target process is not existed,need to load");
    }
    // 关闭句柄
    SAFE_CLOSE_HANDLE(hProcessSnap, INVALID_HANDLE_VALUE);
    return existFlag;
}

bool ProcessPreload::SetProcessWorkingSet(DWORD processId)
{
    if (processId == 0) {
        LOG_ERR("[PRELOAD]set process workingset, process not exist");
        return false;
    }
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
    if (hProcess != INVALID_HANDLE_VALUE) {
        LOG_INFO("[PRELOAD]set process workingset start");
        SetProcessWorkingSetSize(hProcess, -1, -1);
        CloseHandle(hProcess);
        hProcess = INVALID_HANDLE_VALUE;
    } else {
        LOG_ERR("[PRELOAD]OpenProcess failed");
        return false;
    }
    return true;
}

BOOL ProcessPreload::KillPreloadProcess(DWORD processID)
{
    LOG_INFO("[PRELOAD] KillProcess Running! processID {}", processID);
    BOOL isCloseSuc = FALSE;
    DWORD pid = 0;
    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, processID);
    if (hProcess == INVALID_HANDLE_VALUE) {
        DWORD errorCode = GetLastError();
        LOG_ERR("[PRELOAD] KillProcess-OpenProcess fail.Error:{}", errorCode);
        return isCloseSuc;
    }
    if (!PostMessage((HWND)hProcess, WM_QUIT, 0, 0) ||
        (WaitForSingleObject(hProcess, WAIT_EXIT_TIME_MS) == WAIT_TIMEOUT)) {
        LOG_INFO("[PRELOAD]PostMessage Error! TerminateProcess start, processID:{}", processID);
        isCloseSuc = TerminateProcess(hProcess, TERMINATE_PROCESS_FLAG);
        if (!isCloseSuc) {
            LOG_ERR("[PRELOAD]TerminateProcess failed(After PostMsg Fail)! processID:{}", processID);
        }
    }
    // 关闭句柄
    SAFE_CLOSE_HANDLE(hProcess, INVALID_HANDLE_VALUE);
    return isCloseSuc;
}

void ProcessPreload::ClosePreloadProcess(DWORD processId)
{
    if (processId == 0) {
        LOG_ERR("[PRELOAD]close process,but process not exist");
        return;
    }
    LOG_INFO("[PRELOAD]close process start");
    BOOL result = KillPreloadProcess(processId);
    if (result) {
        LOG_INFO("[PRELOAD]process kill sucess");
        if (processId == g_processIdAcad) {
            g_isProcessHasShowAcad = false;
        }
    } else {
        LOG_ERR("[PRELOAD]process kill failed");
    }
    return;
}

void ProcessPreload::CloseAllPreloadProcess()
{
    LOG_INFO("[PRELOAD]close all preload process start");
    if (g_processIdAcad != 0) {
        ClosePreloadProcess(g_processIdAcad);
        g_processIdAcad = 0;
    }
    if (g_processIdXmind != 0 && !g_isProcessHasShowXmind) {
        std::vector<DWORD> xmindProcess =
            PERF_PROCESS_PRELOAD.GetAllProcessIdByName(COMMON_PRELOAD_PROCESS[XMIND_INDEX]);
        for (auto xmindPro : xmindProcess) {
            ClosePreloadProcess(xmindPro);
        }
        g_processIdXmind = 0;
    }
    DWORD exitCode = 1;
    std::string psWindowLine = PS_PRE_PROCESS_WNDCONTROL_EXE_PATH + ACTION_PS_WINDOW;
    GuestUtils::ExecCmdWithExitCode(psWindowLine, true, exitCode);
    if (exitCode != 0 || !g_isProcessHasShowPs) {
        std::vector<DWORD> psProcess = PERF_PROCESS_PRELOAD.GetAllProcessIdByName(PHOTOSHOP_PRE);
        for (auto psPro : psProcess) {
            ClosePreloadProcess(psPro);
        }
    }
    g_isProcessHasShowAcad = false;
    g_isAcadPreloadTimerExist = false;
    g_isAcadNeedCloseTimerExist = false;
    g_preloadTimerCount = 0;
    g_isProcessNeedCloseCount = 0;
    g_isProcessHasShowXmind = false;
    g_isXmindPreloadTimerExist = false;
    g_isXmindNeedCloseTimerExist = false;
    g_isXmindFilePreloadOpen = false;
    g_xmindPreloadTimerCount = 0;
    g_xmindFileCloseTimerCount = 0;
    g_isXmindProcessNeedCloseCount = 0;
    g_isProcessHasShowPs = false;
    g_isPsTimmerStart = false;
    g_pspreloadTimerCount = 0;
    LOG_INFO("[PRELOAD]close all preload process end");
}

void ProcessPreload::GetTokenByProcessId(HANDLE &hToken, DWORD processId)
{
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId);
    if (hProcess != INVALID_HANDLE_VALUE) {
        if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken)) {
            LOG_ERR("[PRELOAD]OpenProcess Tk failed: ");
        }
        SAFE_CLOSE_HANDLE(hProcess, INVALID_HANDLE_VALUE);
    }
    return;
}

BOOL ProcessPreload::GetUserTokenByProcessName(HANDLE &hToken, const std::string &name)
{
    if (name.length() == 0) {
        return FALSE;
    }
    PROCESSENTRY32 pe32 = {0};
    pe32.dwSize = sizeof(PROCESSENTRY32W);

    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        LOG_ERR("[PRELOAD]get token by process name,hProcessSnap is null");
        return FALSE;
    }
    BOOL bRet = ::Process32First(hProcessSnap, &pe32);
    while (bRet) {
        const std::string szExeFile = pe32.szExeFile;
        if ((strncmp(szExeFile.c_str(), name.c_str(), strlen(szExeFile.c_str())) == 0) ||
            (strncmp(szExeFile.c_str(), name.c_str(), strlen(name.c_str())) == 0)) {
            GetTokenByProcessId(hToken, pe32.th32ProcessID);
            break;
        }
        // 获取快照中下一条信息
        bRet = ::Process32Next(hProcessSnap, &pe32);
    }
    // 关闭句柄
    SAFE_CLOSE_HANDLE(hProcessSnap, INVALID_HANDLE_VALUE);
    return TRUE;
}

bool ProcessPreload::ExecCmdWithWithoutTimeOutAsUser(
    const std::string &cmd, const std::string &processName, const std::string &appName, bool showBool)
{
    STARTUPINFO si = {0};
    SecureZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    if (!showBool) {
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;
    }
    PROCESS_INFORMATION pi;
    LOG_INFO("[PRELOAD]excute cmd as user start");
    if (!CreateProcessAsCurretUser(cmd, processName, si, pi, appName)) {
        LOG_ERR("[PRELOAD]excute cmd create process as user failed");
        return false;
    }
    return true;
}

bool ProcessPreload::CreateProcessAsCurretUser(const std::string &cmd, const std::string &processName, STARTUPINFO &si,
    PROCESS_INFORMATION &pi, const std::string &appName)
{
    HANDLE token = NULL;
    HANDLE tokenDup = NULL;
    BOOL ret = FALSE;
    LPVOID environment = NULL;
    ret = GetUserTokenByProcessName(token, processName);
    if (token == INVALID_HANDLE_VALUE || ret == FALSE) {
        LOG_ERR("[PRELOAD]get token by name failed");
        return false;
    }
    LOG_INFO("[PRELOAD]create process as user start");
    ret = DuplicateTokenEx(token, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &tokenDup);
    if (!ret) {
        LOG_ERR("[PRELOAD]DuplicateTokenEx failed");
        SAFE_CLOSE_HANDLE(token, INVALID_HANDLE_VALUE);
        return false;
    }
    SAFE_CLOSE_HANDLE(token, INVALID_HANDLE_VALUE);

    // 创建新环境变量
    ret = CreateEnvironmentBlock(&environment, tokenDup, false);
    if (!ret) {
        SAFE_CLOSE_HANDLE(tokenDup, INVALID_HANDLE_VALUE);
        LOG_ERR("[PRELOAD]create environment block failed");
        return false;
    }
    DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT;
    ret = CreateProcessAsUser(tokenDup, NULL, const_cast<char *>(cmd.c_str()), NULL, NULL,
        FALSE, dwCreationFlags, environment, NULL, &si, &pi);
    if (!ret) {
        LOG_ERR("[PRELOAD]excute create process as user failed");
    } else {
        LOG_INFO("[PRELOAD]excute create process as user success");
        if (appName == PNAME_AUTOCAD) {
            g_processIdAcad = pi.dwProcessId;
        } else if (appName == PNAME_XMIND) {
            g_processIdXmind = pi.dwProcessId;
        }
    }
    if (environment != nullptr) {
        DestroyEnvironmentBlock(environment);
    }
    SAFE_CLOSE_HANDLE(tokenDup, INVALID_HANDLE_VALUE);
    SAFE_CLOSE_HANDLE(pi.hProcess, INVALID_HANDLE_VALUE);
    SAFE_CLOSE_HANDLE(pi.hThread, INVALID_HANDLE_VALUE);
    return true;
}

void ProcessPreload::PreloadProcessHide(const std::string &name)
{
    LOG_INFO("[PRELOAD]process preload start");
    if (name == COMMON_PRELOAD_PROCESS[ACAD_INDEX]) {
        const std::string szCmdLine = g_processCmdlineAcad + START_NOLOGO;
        int beginIdx = szCmdLine.find("exe");
        beginIdx = beginIdx == std::string::npos ? 0 : beginIdx;
        LOG_INFO("acad cmd args: {}", szCmdLine.substr(beginIdx + 1, szCmdLine.length() - beginIdx + 1));
        bool ret = ExecCmdWithWithoutTimeOutAsUser(szCmdLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_AUTOCAD, false);
        if (!ret) {
            LOG_ERR("[PRELOAD]ExecCmdWithWithoutTimeOutAsUser failed");
            return;
        }
        g_isProcessHasShowAcad = false;
        LOG_INFO("[PRELOAD]acad process has not show, need start suspend and checkClose timer");
        g_preloadTimerCount = PRELOAD_MAX_DURATION_ONE_HOURS_AND_FIFTY_EIGHT / CHECK_PROCESS_DUE_THIRTY_SECONDS;
        g_isProcessNeedCloseCount = PRELOAD_MAX_DURATION_TWO_HOURS / CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD;
        std::function<void()> suspendFunc = ProcessSuspendAndSetMemFunc;
        PreloadSuspendTimerTask(suspendFunc, CHECK_PROCESS_DUE_THIRTY_SECONDS);
        if (!g_isAcadNeedCloseTimerExist) {
            std::function<void()> checkNeedCloseFunc = ProcessCheckNeedCloseTimerFunc;
            PreloadCheckIsNeedCloseTimerTask(checkNeedCloseFunc, CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD);
        }
        return;
    } else if (name == COMMON_PRELOAD_PROCESS[XMIND_INDEX]) {
        const std::string szCmdLine = g_processCmdlineXmind + START_NOLOGO;
        bool ret = ExecCmdWithWithoutTimeOutAsUser(szCmdLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_XMIND, false);
        if (!ret) {
            LOG_ERR("[PRELOAD]ExecCmdWithWithoutTimeOutAsUser failed");
            return;
        }
        g_isProcessHasShowXmind = false;
        LOG_INFO("[PRELOAD]xmind process has not show, need start suspend and checkClose timer");
        g_xmindPreloadTimerCount = PRELOAD_MAX_DURATION_ONE_HOURS_AND_FIFTY_EIGHT / CHECK_PROCESS_DUE_THIRTY_SECONDS;
        g_isXmindProcessNeedCloseCount = PRELOAD_MAX_DURATION_TWO_HOURS / CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD;
        std::function<void()> suspendFunc = std::bind(&ProcessPreload::XmindProcessSuspendAndSetMemFunc, this);
        XmindPreloadSuspendTimerTask(suspendFunc, CHECK_PROCESS_DUE_THIRTY_SECONDS);
        if (!g_isXmindNeedCloseTimerExist) {
            std::function<void()> checkNeedCloseFunc = std::bind(
                &ProcessPreload::XmindProcessCheckNeedCloseTimerFunc, this);
            XmindPreloadCheckIsNeedCloseTimerTask(checkNeedCloseFunc, CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD);
        }
        return;
    }
}

bool ProcessPreload::MemoryUsageLimitExceed()
{
    MEMORYSTATUSEX statex = {0};
    statex.dwLength = sizeof(statex);
    GlobalMemoryStatusEx(&statex);
    LOG_INFO("[PRELOAD - MemoryUsageLimitExceed] - current mem usage: {}, limit: {}", statex.dwMemoryLoad,
             SYS_MEMORY_THRESHOLD);
    return statex.dwMemoryLoad > SYS_MEMORY_THRESHOLD;
}

bool ProcessPreload::ProcessPreloadEntry(
    std::string &appName, DWORD processId, std::string &cmdLine, std::string &appPath)
{
    LOG_INFO("[PRELOAD]enter ProcessPreloadEntry.the appName {}", appName);
    bool isAPPNeedPreload = false;
    const std::string processName = appName;
    for (const std::string &str : NEED_PRELOAD_PROCESS) {
        const std::string processName = appName;
        if ((strncmp(str.c_str(), processName.c_str(), strlen(str.c_str())) == 0) ||
            (strncmp(str.c_str(), processName.c_str(), strlen(processName.c_str())) == 0)) {
            isAPPNeedPreload = true;
            LOG_INFO("[PRELOAD]appName match,need to be preload.");
            break;
        }
    }
    if (!isAPPNeedPreload) {
        LOG_ERR("[PRELOAD]skip, app name doesn't matched.");
        return true;
    }
    if (cmdLine.length() == 0) {
        LOG_ERR("[PRELOAD]process cmdLine is null return.");
        return true;
    }
    if (processName == NEED_PRELOAD_PROCESS[ACAD_PRELOAD_INDEX]) {
        LOG_INFO("[PRELOAD]acad match, need to be preloaded");
        ProcessAutoCadPreloadEntry(appName, processId, cmdLine, appPath);
    } else if (processName == NEED_PRELOAD_PROCESS[PS_PRELOAD_INDEX]) {
        LOG_INFO("[PRELOAD]photoshop match, need to be preloaded.");
        ProcessPsPreloadEntry(appName, processId, cmdLine);
    } else if (processName == NEED_PRELOAD_PROCESS[XMIND_PRELOAD_INDEX]) {
        LOG_INFO("[PRELOAD]xmind match, need to be preloaded");
        ProcessXmindPreloadEntry(appName, processId, cmdLine, appPath);
    }
    return true;
}

void ProcessPreload::KillPhotoShopResidue()
{
    if (!g_isPsPreload) {
        DWORD exitCode = 1;
        std::string psWindowLine = PS_PRE_PROCESS_WNDCONTROL_EXE_PATH + ACTION_PS_WINDOW;
        GuestUtils::ExecCmdWithExitCode(psWindowLine, true, exitCode);
        if (exitCode != 0) {
            LOG_INFO("[PRELOAD] - photoshop process residue detected, killing redundant process...");
            std::vector<DWORD> psProcess = PERF_PROCESS_PRELOAD.GetAllProcessIdByName(PHOTOSHOP_PRE);
            for (auto psPro : psProcess) {
                ClosePreloadProcess(psPro);
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_EXIT_TIME_MS));
        }
    }
}

void ProcessPreload::ProcessPsPreloadEntryTimerFunc(std::string &cmdLinePs)
{
    KillPhotoShopResidue();
    if(MemoryUsageLimitExceed()) {
        LOG_INFO("[PRELOAD] - process creation exit due to memory usage limit exceed.");
        return;
    }
    if (!PERF_PROCESS_PRELOAD.IsTargetProcessExist(PHOTOSHOP_PRE)) {
        g_isPsPreload = true;
        LOG_INFO("[PRELOAD]photoshop not preload , use ps_precesss_control.exe to startup ");
        std::string psline = PS_PRE_PROCESS_WNDCONTROL_EXE_PATH + ACTION_PS_START + cmdLinePs;
        g_pspreloadTimerCount = PSTIMER;
        std::string username;
        if (GuestUtils::GetCurrentUsername(username)) {
            char workspacePrefsPath[MAX_PATH] = { 0 };
            if (sprintf_s(workspacePrefsPath, MAX_PATH,
                PHOTOSHOP_WORKSPACE_PREFS_PSP_PATH_FORMAT.c_str(), username.c_str()) > 0) {
                LOG_INFO("[PRELOAD] - Resuming default workspace layout.");
                DeleteFile(workspacePrefsPath);
            }
        }
        ExecCmdWithWithoutTimeOutAsUser(psline, PRELOAD_EXPLORER_EXE_NAME, PNAME_PHOTOSHOP, false);
        g_isProcessHasShowPs = false;
    }
}

std::vector<DWORD> ProcessPreload::GetAllProcessIdByName(const std::string &name)
{
    PROCESSENTRY32 pe32 = {0};
    pe32.dwSize = sizeof(PROCESSENTRY32);
    std::vector<DWORD> allProcessId;
    LOG_INFO("[PRELOAD]target process check start");
    // 获取全部进程快照
    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) {
        LOG_ERR("[PRELOAD]target process check hProcessSnap is null or invalid");
        return allProcessId;
    }
    // 获取快照中第一条信息
    BOOL bRet = ::Process32First(hProcessSnap, &pe32);
    while (bRet) {
        const std::string szExeFile = pe32.szExeFile;
        int minLen = szExeFile.size() < name.size() ? szExeFile.size() : name.size();
        if ((strncmp(szExeFile.c_str(), name.c_str(), minLen) == 0)) {
            allProcessId.push_back(pe32.th32ProcessID);
        }
        // 获取快照中下一条信息
        bRet = ::Process32Next(hProcessSnap, &pe32);
    }
    // 关闭句柄
    SAFE_CLOSE_HANDLE(hProcessSnap, INVALID_HANDLE_VALUE);
    return allProcessId;
}

void ProcessPreload::ProcessPsPreloadEntry(std::string &appName, DWORD processId, std::string &cmdLine)
{
    std::string cmdLinePs = cmdLine;
    if (!g_isPsTimmerStart) {
        LOG_INFO("[PRELOAD]first click,  start pstimmer");
        g_isProcessHasShowPs = true;
        g_pspreloadTimerCount = PSTIMER;
        std::function<void()> func = std::bind(&ProcessPreload::ProcessPsPreloadEntryTimerFunc, this, cmdLinePs);
        PsPreloadTimerTask(func, CHECK_PROCESS_DUE_THIRTY_SECONDS);
        return;
    }
    if ((!PERF_PROCESS_PRELOAD.IsTargetProcessExist(PS_CONTROL))) {
        LOG_INFO("[PRELOAD]second click, ps has started, use ps_precesss_control.exe to show ps");
        std::string psline = PS_PRE_PROCESS_WNDCONTROL_EXE_PATH + PARAM_SEP + ACTION_SHOW + PARAM_SEP + cmdLinePs;
        ExecCmdWithWithoutTimeOutAsUser(psline, PRELOAD_EXPLORER_EXE_NAME, PNAME_PHOTOSHOP, false);
        g_isPsPreload = false;
        g_isProcessHasShowPs = true;
    } else {
        LOG_INFO("[PRELOAD]second click, ps is being started, kill ps_precesss_control.exe and show ps normal");
        std::vector<DWORD> psPreProcess = PERF_PROCESS_PRELOAD.GetAllProcessIdByName(PS_CONTROL);
        if (!psPreProcess.empty()) {
            LOG_INFO("[PRELOAD]psPreProcess[0]{}", psPreProcess[0]);
            ClosePreloadProcess(psPreProcess[0]);
        }
        std::vector<DWORD> psProcess = PERF_PROCESS_PRELOAD.GetAllProcessIdByName(PHOTOSHOP_PRE);
        for (auto psPro : psProcess) {
            ClosePreloadProcess(psPro);
        }
        ExecCmdWithWithoutTimeOutAsUser(cmdLinePs, PRELOAD_EXPLORER_EXE_NAME, PNAME_PHOTOSHOP, false);
        g_isPsPreload = false;
        g_isProcessHasShowPs = true;
    }
    return;
}

bool ProcessPreload::ProcessAcadCheckAndDealPreload(std::string &appName, DWORD processId, std::string &cmdLine)
{
    LOG_INFO("[PRELOAD]ProcessAcadCheckAndDealPreload start!");
    if (cmdLine.empty()) {
        LOG_ERR("[PRELOAD]ProcessPreload cmdLine is empty!");
        return false;
    }
    if (g_processIdAcad == 0) {
        g_isProcessHasShowAcad = false;
        if ((cmdLine != PRELOAD_UNUSE_CMD_LINE)) {
            if (!GetFilePath(cmdLine)) {
                LOG_ERR("[PRELOAD]ProcessPreload cmdLine is not match");
                g_processCmdlineAcad = "";
                return false;
            }
            g_processCmdlineAcad = cmdLine;
        }
        g_preloadTimerCount = PRELOAD_MAX_DURATION_ONE_HOURS_AND_FIFTY_EIGHT / CHECK_PROCESS_DUE_THIRTY_SECONDS;
        g_isProcessNeedCloseCount = PRELOAD_MAX_DURATION_TWO_HOURS / CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD;
        LOG_INFO("[PRELOAD]process name match,start timer task to check process is need to preload.");
        if (!g_isAcadPreloadTimerExist) {
            std::function<void()> func = std::bind(&ProcessPreload::ProcessPreloadEntryTimerFunc, this);
            PreloadTimerTask(func, CHECK_PROCESS_DUE_THIRTY_SECONDS);
        }
        return true;
    }
    return false;
}

bool ProcessPreload::ProcessAcadCheckAndDealFileOpen(
    std::string &appName, DWORD processId, std::string &cmdLine, std::string &appPath)
{
    LOG_ERR("[PRELOAD]ProcessAcadCheckAndDealFileOpen start!");
    if (appPath.empty()) {
        LOG_ERR("[PRELOAD]ProcessPreload appPath is empty!");
        return false;
    }
    if ((processId == g_processIdAcad) && (g_processIdAcad != 0) && (cmdLine == PRELOAD_UNUSE_CMD_LINE)) {
        g_processFilePath = appPath;
        LOG_INFO("[PRELOAD]file choose and open,just need to resume.");
        bool bRet = PERF_PROCESS_PRELOAD.SuspendOrResumeProcessByName(appName, g_processIdAcad, false);
        if (!bRet) {
            ClosePreloadProcess(g_processIdAcad);
            LOG_ERR("[PRELOAD]Process resume excute failed");
            return false;
        }
        std::function<void()> ResetPidFunc = ProcessPreloadResetPidTimerFunc;
        PreloadResetPidTimerTask(ResetPidFunc, CHECK_PROCESS_RESET_PID_TWO_SECONDS);
        g_isProcessHasShowAcad = true;
        return true;
    }
    if ((processId != g_processIdAcad) && (g_isProcessHasShowAcad) && (g_processIdAcad != 0)) {
        LOG_INFO("[PRELOAD]process will open file create new redundancy process,need close it.");
        if (!TextUtils::FilePatchCheck(g_processFilePath)) {
            LOG_ERR("[PRELOAD]ProcessPreload file Path is not realpath");
            g_processCmdlineAcad = "";
            return false;
        }
        ClosePreloadProcess(processId);
        LOG_INFO("[PRELOAD]process reopen file.");
        std::string showAndReopenLine = PRE_PROCESS_WNDCONTROL_EXE_PATH + PARAM_SEP + ACTION_SHOW_AND_REOPEN_FILE +
            PARAM_SEP + g_processFilePath + PARAM_SEP + COMMON_PRELOAD_PROCESS[ACAD_INDEX];
        ExecCmdWithWithoutTimeOutAsUser(showAndReopenLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_AUTOCAD, false);
        g_processIdAcad = 0;
        g_processFilePath = "";
        return true;
    }
    return false;
}

bool ProcessPreload::ProcessAcadCheckAndDealAppStart(
    std::string &appName, DWORD processId, std::string &cmdLine, std::string &appPath)
{
    LOG_INFO("[PRELOAD]ProcessAcadCheckAndDealAppStart start!");
    if ((processId != g_processIdAcad) && (!g_isProcessHasShowAcad) && (g_processIdAcad != 0)) {
        double preLoadProcessDuration = GetProcessRunningDuration(g_processIdAcad);
        LOG_INFO("[PRELOAD]process start duration :{}.", preLoadProcessDuration);
        if (preLoadProcessDuration >= CHECK_PROCESS_DUE_THIRTY_SECONDS) {
            LOG_INFO("[PRELOAD]process has started , process id change to:{}.", g_processIdAcad);
            bool bRet = PERF_PROCESS_PRELOAD.SuspendOrResumeProcessByName(appName, g_processIdAcad, false);
            if (!bRet) {
                ClosePreloadProcess(g_processIdAcad);
                LOG_ERR("[PRELOAD]process Resume excute failed");
                return false;
            }
            ClosePreloadProcess(processId);
            std::string lpStrAppPath = FilePathTranslation(appPath);
            std::string showLine = PRE_PROCESS_WNDCONTROL_EXE_PATH + PARAM_SEP + ACTION_SHOW + PARAM_SEP +
                lpStrAppPath + PARAM_SEP + COMMON_PRELOAD_PROCESS[ACAD_INDEX];
            ExecCmdWithWithoutTimeOutAsUser(showLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_AUTOCAD, false);
            g_processIdAcad = 0;
            g_isProcessHasShowAcad = true;
        } else {
            ClosePreloadProcess(g_processIdAcad);
        }
        return true;
    }
    return false;
}

void ProcessPreload::ProcessAutoCadPreloadEntry(
    std::string &appName, DWORD processId, std::string &cmdLine, std::string &appPath)
{
    LOG_INFO("[PRELOAD]ProcessAutoCadPreloadEntry start.");
    if (ProcessAcadCheckAndDealPreload(appName, processId, cmdLine)) {
        LOG_INFO("[PRELOAD]process preload timer deal success.");
        return;
    }
    if (ProcessAcadCheckAndDealFileOpen(appName, processId, cmdLine, appPath)) {
        LOG_INFO("[PRELOAD]process file open deal success.");
        return;
    }
    if (ProcessAcadCheckAndDealAppStart(appName, processId, cmdLine, appPath)) {
        LOG_INFO("[PRELOAD]process app show deal success.");
        return;
    }
    return;
}

void ProcessPreload::ProcessXmindPreloadEntry(
    std::string &appName, DWORD processId, std::string &cmdLine, std::string &appPath)
{
    LOG_INFO("[PRELOAD]ProcessXmindPreloadEntry start.");
    if (ProcessXmindCheckAndDealFileOpen(processId, cmdLine)) {
        LOG_INFO("[PRELOAD]xmind process file open deal success.");
        return;
    }
    if (ProcessXmindCheckAndDealPreload(appPath)) {
        LOG_INFO("[PRELOAD]xmind process preload timer deal success.");
        return;
    }
    if (ProcessXmindCheckAndDealAppStart(processId, appPath)) {
        LOG_INFO("[PRELOAD]xmind process app show deal success.");
        return;
    }
    LOG_ERR("[PRELOAD]xmind process preload entry failed.");
    return;
}

bool ProcessPreload::ProcessXmindCheckAndDealFileOpen(DWORD processId, std::string &cmdLine)
{
    if (g_processIdXmind == 0) {
        return false;
    }
    if (cmdLine.rfind(XMIND_FILE_SUFFIX) != std::string::npos) {
        if (g_isXmindFilePreloadOpen) {
            LOG_INFO("[PRELOAD]the other xmind file is already open, exit.");
            return true;
        }
        // get current xmind process number
        std::string xmindProcessName = COMMON_PRELOAD_PROCESS[XMIND_INDEX];
        std::vector<DWORD> xmindProcessId = GetAllProcessIdByName(xmindProcessName);
        int ProcessIdNumber = xmindProcessId.size();
        LOG_INFO("[PRELOAD]the number of xmind processes is {}", ProcessIdNumber);
        // get xmind file path
        std::vector<std::string> fragments = PERF_UTILS.Split(cmdLine, PARAM_MARKER);
        std::string xmindFilePath = "";
        for (auto it : fragments) {
            if (it.rfind(XMIND_FILE_SUFFIX) != std::string::npos && FilePathIsVaildCheck(it)) {
                xmindFilePath = PARAM_MARKER + it + PARAM_MARKER;
                break;
            }
        }
        if (xmindFilePath.empty()) {
            LOG_INFO("[PRELOAD]the xmind file path is invaild.");
        }
        // open xmind file according to the number of processes
        if (ProcessIdNumber < XMIND_BASIC_PROCESS) {
            LOG_INFO("[PRELOAD]xmind process is incomplete, need to wait all xmind process load.");
            std::vector<DWORD> xmindProcess =
                PERF_PROCESS_PRELOAD.GetAllProcessIdByName(COMMON_PRELOAD_PROCESS[XMIND_INDEX]);
            for (auto xmindPro : xmindProcess) {
                ClosePreloadProcess(xmindPro);
            }
            ExecCmdWithWithoutTimeOutAsUser(cmdLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_XMIND, true);
            g_isXmindFilePreloadOpen = false;
            g_isProcessHasShowXmind = false;
            g_processIdXmind = 0;
            return true;
        }
        if (ProcessIdNumber >= XMIND_BASIC_PROCESS) {
            LOG_INFO("[PRELOAD]xmind process is complete, just need to open the xmind file.");
            ClosePreloadProcess(processId);
            for (auto it : xmindProcessId) {
                SuspendOrResumeProcessByName(xmindProcessName, it, false);
            }
            OpenXmindFileByFilePath(xmindFilePath, xmindProcessName);
            return true;
        }
    }
    return false;
}

bool ProcessPreload::FilePathIsVaildCheck(std::string &filePath)
{
    LOG_INFO("[PRELOAD]check file path is vaild.");
    DWORD attr = GetFileAttributes(filePath.c_str());
    if (attr == INVALID_FILE_ATTRIBUTES) { // 是否存在
        return false;
    }
    if (attr & FILE_ATTRIBUTE_DIRECTORY) { // 是否是目录
        return false;
    }
    if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { // 是否为链接
        return false;
    }
    return true; // 确认是个普通文件
}

bool ProcessPreload::ProcessXmindCheckAndDealPreload(std::string &cmdLine)
{
    LOG_INFO("[PRELOAD]ProcessXmindCheckAndDealPreload start!");
    if (cmdLine.empty()) {
        LOG_ERR("[PRELOAD]XmindProcessPreload cmdLine is empty!");
        return false;
    }
    if (g_processIdXmind == 0) {
        g_isProcessHasShowXmind = false;
        if (!GetFilePath(cmdLine)) {
            LOG_ERR("[PRELOAD]XmindProcessPreload cmdLine is not match");
            g_processCmdlineXmind = "";
            return false;
        }
        g_processCmdlineXmind = cmdLine;
        g_xmindPreloadTimerCount = PRELOAD_MAX_DURATION_ONE_HOURS_AND_FIFTY_EIGHT / CHECK_PROCESS_DUE_THIRTY_SECONDS;
        g_isXmindProcessNeedCloseCount = PRELOAD_MAX_DURATION_TWO_HOURS / CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD;
        LOG_INFO("[PRELOAD]xmind process name match,start timer task to check process is need to preload.");
        if (!g_isXmindPreloadTimerExist) {
            std::function<void()> func = std::bind(&ProcessPreload::XmindProcessPreloadEntryTimerFunc, this);
            XmindPreloadTimerTask(func, CHECK_PROCESS_DUE_THIRTY_SECONDS);
        }
        return true;
    }
    return false;
}

bool ProcessPreload::ProcessXmindCheckAndDealAppStart(DWORD processId, std::string &appPath)
{
    LOG_INFO("[PRELOAD]ProcessXmindCheckAndDealAppStart start!");
    if ((processId != g_processIdXmind) && (!g_isProcessHasShowXmind) && (g_processIdXmind != 0)) {
        double preLoadProcessDuration = GetProcessRunningDuration(g_processIdXmind);
        LOG_INFO("[PRELOAD]process start duration :{}.", preLoadProcessDuration);
        if (preLoadProcessDuration >= CHECK_PROCESS_DUE_THIRTY_SECONDS) {
            HandleXmindAppStart(processId, appPath);
        } else {
            LOG_INFO("[PRELOAD]xmind process has not started, use the source message and open xmind");
            ClosePreloadProcess(g_processIdXmind);
            g_processIdXmind = 0;
            g_isProcessHasShowXmind = false;
        }
        return true;
    }
    return false;
}

void ProcessPreload::HandleXmindAppStart(DWORD processId, std::string &appPath)
{
    LOG_INFO("[PRELOAD]xmind process has started , process id change to:{}.", g_processIdXmind);
    std::string xmindProcessName = COMMON_PRELOAD_PROCESS[XMIND_INDEX];
    std::vector<DWORD> xmindProcessId = GetAllProcessIdByName(xmindProcessName);
    for (auto it : xmindProcessId) {
        bool bRet = SuspendOrResumeProcessByName(xmindProcessName, it, false);
        if (!bRet) {
            CloseAllXmindLoadProcess(xmindProcessId);
            ExecCmdWithWithoutTimeOutAsUser(appPath, PRELOAD_EXPLORER_EXE_NAME, PNAME_XMIND, true);
            g_processIdXmind = 0;
            g_isProcessHasShowXmind = false;
            LOG_ERR("[PRELOAD]xmind process Resume excute failed");
            return;
        }
    }
    ClosePreloadProcess(processId);
    std::string lpStrAppPath = FilePathTranslation(appPath);
    std::string showLine = PRE_PROCESS_WNDCONTROL_EXE_PATH + PARAM_SEP + ACTION_SHOW + PARAM_SEP +
        lpStrAppPath + PARAM_SEP + xmindProcessName;
    ExecCmdWithWithoutTimeOutAsUser(showLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_XMIND, false);
    g_processIdXmind = 0;
    g_isProcessHasShowXmind = true;
}

void ProcessPreload::OpenXmindFileByFilePath(std::string &xmindFilePath, std::string &xmindProcessName)
{
    LOG_INFO("[PRELOAD]open xmind file by file path.");
    g_isXmindFilePreloadOpen = true;
    g_isProcessHasShowXmind = true;
    std::string showAndReopenLine = PRE_PROCESS_WNDCONTROL_EXE_PATH + PARAM_SEP + ACTION_SHOW_AND_REOPEN_FILE +
        PARAM_SEP + xmindFilePath + PARAM_SEP + xmindProcessName;
    ExecCmdWithWithoutTimeOutAsUser(showAndReopenLine, PRELOAD_EXPLORER_EXE_NAME, PNAME_XMIND, false);
    g_xmindFileCloseTimerCount = PRELOAD_MAX_DURATION_TWO_HOURS / CHECK_PROCESS_IS_NEED_CLOSE_ONE_MINUTE_PERIOD;
    if (g_isXmindPreloadTimerExist) {
        std::function<void()> func = std::bind(&ProcessPreload::XmindProcessCloseTimerFunc, this);
        XmindFileCloseTimerTask(func, CHECK_PROCESS_DUE_THIRTY_SECONDS);
    }
    g_processIdXmind = 0;
}

void ProcessPreload::CloseAllXmindLoadProcess(std::vector<DWORD> &xmindProcessId)
{
    LOG_INFO("[PRELOAD]close all xmind load process.");
    for (auto it : xmindProcessId) {
        ClosePreloadProcess(it);
    }
}

void ProcessPreload::XmindPreloadTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]xmind process preload timer start");
    g_isXmindPreloadTimerExist = true;
    std::thread([interval, callback]() {
        while (g_xmindPreloadTimerCount > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(interval));
            --g_xmindPreloadTimerCount;
            if (g_xmindPreloadTimerCount > 0) {
                callback();  // 调用回调函数
            }
        }
        g_isXmindPreloadTimerExist = false;
        LOG_INFO("[PRELOAD]xmind process preload timer end");
    }).detach();
}

void ProcessPreload::XmindPreloadSuspendTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]xmind process preload suspend timer start");
    std::thread([interval, callback]() {
        std::this_thread::sleep_for(std::chrono::seconds(interval));
        if (!g_isProcessHasShowXmind) {
            callback();
        }
        LOG_INFO("[PRELOAD]xmind process preload suspend timer end");
    }).detach();
}

void ProcessPreload::XmindFileCloseTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]xmind file close timer start");
    std::thread([interval, callback]() {
        while (g_xmindFileCloseTimerCount > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(interval));
            --g_xmindFileCloseTimerCount;
            if (!g_isXmindFilePreloadOpen) {
                LOG_INFO("the xmind file is close, need to stop timer task.");
                break;
            }
            LOG_INFO("the xmind file is open, need to start callback.");
            if (g_xmindFileCloseTimerCount > 0) {
                callback();  // 调用回调函数
            }
        }
        LOG_INFO("[PRELOAD]xmind process preload timer end");
    }).detach();
}

void ProcessPreload::XmindPreloadCheckIsNeedCloseTimerTask(std::function<void()> callback, int interval)
{
    LOG_INFO("[PRELOAD]xmind process check is need close timer start");
    g_isXmindNeedCloseTimerExist = true;
    std::thread([interval, callback]() {
        while (g_isXmindProcessNeedCloseCount > 0) {
            std::this_thread::sleep_for(std::chrono::seconds(interval));
            --g_isXmindProcessNeedCloseCount;
        }
        callback();  // 调用回调函数
        g_isXmindNeedCloseTimerExist = false;
        LOG_INFO("[PRELOAD]xmind process check is need close timer end");
    }).detach();
}

void ProcessPreload::XmindProcessPreloadEntryTimerFunc()
{
    BOOL bRet = IsTargetProcessExist(COMMON_PRELOAD_PROCESS[XMIND_INDEX]);
    if (!bRet && !MemoryUsageLimitExceed()) {
        PreloadProcessHide(COMMON_PRELOAD_PROCESS[XMIND_INDEX]);
    }
}

void ProcessPreload::XmindProcessSuspendAndSetMemFunc()
{
    LOG_INFO("[PRELOAD]xmind process Suspend&WorkingSet start excute");
    std::string xmindProcessName = COMMON_PRELOAD_PROCESS[XMIND_INDEX];
    if (g_processIdXmind == 0 || g_isProcessHasShowXmind) {
        return;
    }
    std::vector<DWORD> xmindProcessId = GetAllProcessIdByName(xmindProcessName);
    if (xmindProcessId.size() < XMIND_BASIC_PROCESS) {
        CloseAllXmindLoadProcess(xmindProcessId);
        g_processIdXmind = 0;
        g_isProcessHasShowXmind = false;
    } else {
        for (auto it : xmindProcessId) {
            ProcessSuspendAndSetMemByProcessId(xmindProcessName, it, true);
        }
    }
}

void ProcessPreload::ProcessSuspendAndSetMemByProcessId(std::string &processName, DWORD processId, bool isNeedSuspend)
{
    LOG_INFO("[PRELOAD]xmind process suspend and set memery by process ID.");
    bool bRet = SuspendOrResumeProcessByName(processName, processId, isNeedSuspend);
    if (!bRet) {
        LOG_INFO("[PRELOAD]xmind process suspend excute failed, the process ID is {}", processId);
    }
    bRet = SetProcessWorkingSet(processId);
    if (!bRet) {
        LOG_INFO("[PRELOAD]xmind process set WorkingSet failed, the process ID is {}", processId);
    }
}

void ProcessPreload::XmindProcessCheckNeedCloseTimerFunc()
{
    if (g_processIdXmind == 0) {
        LOG_INFO("[PRELOAD]g_processIdXmind is not valid.");
        return;
    }
    LOG_INFO("[PRELOAD]xmind process check is need close timer start excute.");
    if (!g_isProcessHasShowXmind) {
        std::string xmindProcessName = COMMON_PRELOAD_PROCESS[XMIND_INDEX];
        std::vector<DWORD> xmindProcessId = GetAllProcessIdByName(xmindProcessName);
        CloseAllXmindLoadProcess(xmindProcessId);
        g_processIdXmind = 0;
        return;
    }
}

void ProcessPreload::XmindProcessCloseTimerFunc()
{
    LOG_INFO("[PRELOAD]xmind process is need close timer start excute when xmind file is closed.");
    std::string xmindProcessName = COMMON_PRELOAD_PROCESS[XMIND_INDEX];
    std::vector<DWORD> xmindProcessId = GetAllProcessIdByName(xmindProcessName);
    if (xmindProcessId.size() <= XMIND_BASIC_PROCESS) {
        LOG_INFO("[PRELOAD] the xmind process is closed, need to close all xmind process.");
        CloseAllXmindLoadProcess(xmindProcessId);
        g_processIdXmind = 0;
        g_isXmindFilePreloadOpen = false;
        g_isProcessHasShowXmind = false;
    }
}

std::string ProcessPreload::FilePathTranslation(std::string &filePathStr)
{
    LPSTR lpStr = (LPSTR) "";
    if (filePathStr.empty()) {
        return std::string(lpStr);
    }
    char str[MAX_PATH] = {0};
    errno_t rc = strcpy_s(str, MAX_PATH, filePathStr.c_str());
    if (rc != OK) {
        return std::string(lpStr);
    }
    lpStr = str;
    PathQuoteSpacesA(lpStr);
    return std::string(lpStr);
}

bool ProcessPreload::GetFilePath(std::string &filePathStr)
{
    bool ret = false;
    if (filePathStr.empty()) {
        return ret;
    }
    int start = 0;
    int end = 0;
    if (filePathStr.find_first_of("\"") == 0) {
        start = SPLIT_BEGIN_POS_QU;
        end = SPLIT_END_POS_QU;
    } else {
        start = SPLIT_BEGIN_POS;
        end = SPLIT_END_POS;
    }
    std::string filePath = filePathStr.substr(start, filePathStr.find_last_of(".") + end);
    ret = TextUtils::FilePatchCheck(filePath);
    return ret;
}

}  // namespace PERF